package com.example.auth.controller;

import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.example.auth.common.R;
import com.example.auth.model.dto.LoginDTO;
import com.example.auth.model.dto.UserInfoDTO;
import com.example.auth.model.entity.OAuthClient;
import com.example.auth.service.OAuthClientService;
import com.example.auth.service.SysUserService;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * OAuth2授权控制器
 */
@RestController
@RequestMapping("/oauth2")
public class OAuth2Controller {

    @Resource
    private OAuthClientService clientService;
    
    @Resource
    private SysUserService userService;

    /**
     * 授权码模式 - 获取授权码
     */
    @GetMapping("/authorize")
    public ModelAndView authorize(
            @RequestParam(required = false) String client_id, 
            @RequestParam(required = false) String response_type, 
            @RequestParam(required = false) String redirect_uri, 
            @RequestParam(required = false) String scope, 
            @RequestParam(required = false) String state) {
        
        System.out.println("进入authorize方法，检查请求参数：");
        System.out.println("client_id: " + client_id);
        System.out.println("response_type: " + response_type);
        System.out.println("redirect_uri: " + redirect_uri);
        
        // 校验参数
        if (client_id == null || response_type == null || redirect_uri == null) {
            return new ModelAndView("redirect:/oauth2/error?message=参数不完整");
        }
        if (!"code".equals(response_type)) {
            return new ModelAndView("redirect:/oauth2/error?message=response_type参数错误");
        }
        
        // 校验客户端
        OAuthClient client = clientService.getByClientId(client_id);
        if (client == null) {
            return new ModelAndView("redirect:/oauth2/error?message=无效的客户端");
        }
        
        // 校验重定向URI
        if (!clientService.validateRedirectUri(client_id, redirect_uri)) {
            return new ModelAndView("redirect:/oauth2/error?message=重定向URI不匹配");
        }
        
        // 如果用户未登录，跳转到登录页
        try {
            // 检查用户是否已登录
            boolean isLogin = StpUtil.isLogin();
            System.out.println("当前登录状态: " + isLogin);
            
            // 获取token信息，便于调试
            try {
                String tokenValue = StpUtil.getTokenValue();
                System.out.println("当前Token值: " + tokenValue);
            } catch (Exception e) {
                System.out.println("获取Token值失败: " + e.getMessage());
            }
            
            if (!isLogin) {
                // 用户未登录，返回登录页面
                System.out.println("用户未登录，跳转到登录页面");
                ModelAndView mav = new ModelAndView("oauth2/login");
                // 保存授权请求参数，以便登录后回到授权页面
                mav.addObject("client_id", client_id);
                mav.addObject("response_type", response_type);
                mav.addObject("redirect_uri", redirect_uri);
                mav.addObject("scope", scope);
                mav.addObject("state", state);
                return mav;
            }
            
            System.out.println("用户已登录，用户ID: " + StpUtil.getLoginIdAsString());
            
            // 如果是自动批准的客户端，直接授权并返回授权码
            if (client.getAutoApprove() == 1) {
                String code = generateAuthCode(client_id, StpUtil.getLoginIdAsLong(), scope);
                String redirectUrl = buildRedirectUrl(redirect_uri, code, state);
                return new ModelAndView("redirect:" + redirectUrl);
            }
            
            // 跳转到授权确认页
            ModelAndView mav = new ModelAndView("oauth2/confirm");
            mav.addObject("client", client);
            mav.addObject("scope", scope);
            mav.addObject("redirect_uri", redirect_uri);
            mav.addObject("state", state);
            mav.addObject("response_type", response_type);
            
            return mav;
        } catch (Exception e) {
            // 捕获未登录异常或其他异常
            System.out.println("发生异常：" + e.getMessage());
            e.printStackTrace();
            ModelAndView mav = new ModelAndView("oauth2/login");
            // 保存授权请求参数，以便登录后回到授权页面
            mav.addObject("client_id", client_id);
            mav.addObject("response_type", response_type);
            mav.addObject("redirect_uri", redirect_uri);
            mav.addObject("scope", scope);
            mav.addObject("state", state);
            return mav;
        }
    }

    /**
     * 授权码模式 - 获取令牌
     */
    @PostMapping("/token")
    public R<Map<String, Object>> token(
            String grant_type, 
            String code, 
            String client_id, 
            String client_secret, 
            String redirect_uri) {
        
        // 校验参数
        if (grant_type == null || code == null || client_id == null || client_secret == null) {
            return R.failed("参数不完整");
        }
        if (!"authorization_code".equals(grant_type)) {
            return R.failed("grant_type参数错误");
        }
        
        // 校验客户端凭证
        if (!clientService.validateClientCredentials(client_id, client_secret)) {
            return R.failed("客户端凭证无效");
        }
        
        // 校验授权码
        Object loginId = validateAuthCode(code, client_id);
        if (loginId == null) {
            return R.failed("无效的授权码");
        }
        
        // 生成访问令牌和刷新令牌
        Map<String, Object> tokenInfo = generateTokens(client_id, loginId);
        
        return R.ok(tokenInfo);
    }

    /**
     * 用户登录（用于OAuth2授权码模式）
     */
    @PostMapping("/doLogin")
    public R<Map<String, Object>> doLogin(@RequestBody LoginDTO loginDTO) {
        // 调用登录服务完成登录
        R<String> loginResult = userService.login(loginDTO);
        
        if (loginResult.getCode() != 200) {
            return R.failed(loginResult.getMessage());
        }

        // 获取登录用户ID - 检查登录服务是否正确设置了登录状态
        boolean loginStatus = StpUtil.isLogin();
        System.out.println("登录服务执行后的登录状态：" + loginStatus);
        
        // 如果登录服务没有正确设置登录状态，则尝试获取token并手动设置
        if (!loginStatus) {
            try {
                // 尝试从返回数据中提取token并解析，或者进行其他方式的登录
                System.out.println("检测到登录服务未设置登录状态，尝试手动处理...");
                // 此处可以根据实际情况修改，例如从返回的token中解析用户ID
                // 或者重新获取用户信息后再登录
                
                // 使用Sa-Token内置的临时登录方案（仅用于演示，实际项目中需谨慎使用）
                // 假设返回的数据格式为：userId@tokenValue
                String tokenData = loginResult.getData();
                if (tokenData != null && tokenData.contains("@")) {
                    String userId = tokenData.split("@")[0];
                    if (userId != null && !userId.isEmpty()) {
                        StpUtil.login(Long.parseLong(userId));
                        System.out.println("手动设置登录状态，用户ID: " + userId);
                    }
                }
            } catch (Exception e) {
                System.err.println("手动设置登录状态失败：" + e.getMessage());
                e.printStackTrace();
            }
        }

        // 返回登录成功信息
        Map<String, Object> result = new HashMap<>();
        result.put("token", loginResult.getData());
        result.put("isLogin", StpUtil.isLogin());  // 再次检查登录状态
        result.put("userId", StpUtil.isLogin() ? StpUtil.getLoginIdAsString() : "未登录");
        
        // 记录日志，便于调试
        System.out.println("用户 " + loginDTO.getUsername() + " 处理完成");
        System.out.println("最终登录状态： " + StpUtil.isLogin());
        if (StpUtil.isLogin()) {
            System.out.println("登录ID: " + StpUtil.getLoginIdAsString());
        }
        
        return R.ok(result);
    }

    /**
     * 确认授权
     */
    @PostMapping("/doConfirm")
    public R<String> doConfirm(String client_id, String scope, String redirect_uri, String state, String response_type) {
        // 校验客户端
        OAuthClient client = clientService.getByClientId(client_id);
        if (client == null) {
            return R.failed("无效的客户端");
        }
        
        // 校验重定向URI
        if (!clientService.validateRedirectUri(client_id, redirect_uri)) {
            return R.failed("无效的重定向URI");
        }
        
        // 如果用户未登录，返回错误
        if (!StpUtil.isLogin()) {
            return R.failed("用户未登录");
        }
        
        // 生成授权码
        String code = generateAuthCode(client_id, StpUtil.getLoginIdAsLong(), scope);
        
        // 构建重定向URL
        String redirectUrl = buildRedirectUrl(redirect_uri, code, state);
        
        return R.ok(redirectUrl);
    }

    /**
     * 获取用户信息，用于校验令牌并返回用户信息
     * 请求格式：/oauth2/userinfo?access_token=xxx
     */
    @GetMapping("/userinfo")
    public R<UserInfoDTO> getUserInfo(String access_token) {
        // 校验令牌
        Object loginId = validateAccessToken(access_token);
        if (loginId == null) {
            return R.failed(R.ResultCode.UNAUTHORIZED.getCode(), "无效的访问令牌");
        }
        
        // 获取用户信息
        Long userId = Long.parseLong(loginId.toString());
        StpUtil.login(userId); // 临时登录，方便获取用户信息
        R<UserInfoDTO> userInfo = userService.getCurrentUserInfo();
        StpUtil.logout(); // 登出
        
        return userInfo;
    }
    
    /**
     * 错误页面
     */
    @GetMapping("/error")
    public R<String> error(String message) {
        return R.failed(message);
    }
    
    /**
     * 生成授权码
     */
    private String generateAuthCode(String clientId, Object loginId, String scope) {
        // 简单实现：生成一个UUID作为授权码，实际项目中应该使用更安全的方式
        String code = java.util.UUID.randomUUID().toString().replace("-", "");
        
        // 将code与用户信息关联，实际项目中应该存储到Redis或数据库
        // 这里简化处理，真实项目中需要存储授权码和相关信息，并设置过期时间
        
        return code;
    }
    
    /**
     * 验证授权码并返回关联的loginId
     */
    private Object validateAuthCode(String code, String clientId) {
        // 简化版：返回一个固定的用户ID，实际项目中应该从存储中查询授权码的有效性和关联信息
        return 1L;
    }
    
    /**
     * 构建重定向URL，带上授权码和状态
     */
    private String buildRedirectUrl(String redirectUri, String code, String state) {
        StringBuilder url = new StringBuilder(redirectUri);
        url.append(redirectUri.contains("?") ? "&" : "?");
        url.append("code=").append(code);
        if (state != null && !state.isEmpty()) {
            url.append("&state=").append(state);
        }
        return url.toString();
    }
    
    /**
     * 生成访问令牌和刷新令牌
     */
    private Map<String, Object> generateTokens(String clientId, Object loginId) {
        Map<String, Object> tokenInfo = new HashMap<>();
        
        // 生成访问令牌和刷新令牌，实际项目中应该使用更安全的方式
        String accessToken = java.util.UUID.randomUUID().toString().replace("-", "");
        String refreshToken = java.util.UUID.randomUUID().toString().replace("-", "");
        
        // 设置令牌过期时间
        OAuthClient client = clientService.getByClientId(clientId);
        int accessTokenExpiresIn = client.getAccessTokenValiditySeconds() != null ? 
                client.getAccessTokenValiditySeconds().intValue() : 3600; // 默认1小时
        
        // 将令牌与用户信息关联，实际项目中应该存储到Redis或数据库
        // 这里简化处理，真实项目中需要存储令牌和相关信息，并设置过期时间
        
        // 构建返回信息
        tokenInfo.put("access_token", accessToken);
        tokenInfo.put("token_type", "bearer");
        tokenInfo.put("expires_in", accessTokenExpiresIn);
        tokenInfo.put("refresh_token", refreshToken);
        
        return tokenInfo;
    }
    
    /**
     * 验证访问令牌并返回关联的loginId
     */
    private Object validateAccessToken(String accessToken) {
        // 简化版：返回一个固定的用户ID，实际项目中应该从存储中查询令牌的有效性和关联信息
        return 1L;
    }
} 